import {EventDispatcher, Spherical} from 'three';
import {TWEEN} from "three/examples/jsm/libs/tween.module.min.js";
import * as THREE from "three";

var MoveLookAtControls = function (camera, domElement) {
    if (domElement === undefined) {
        console.warn('THREE.MoveLookAtControls: The second parameter "domElement" is now mandatory.');
        domElement = document.body;
    }

    this.customParams = {}

    this.domElement = domElement;

    // Set to constrain the pitch of the camera 设置为限制摄像机的俯仰
    // Range is 0 to Math.PI radians 范围是0到Math.PI弧度
    this.minPolarAngle = 0; // radians
    this.maxPolarAngle = Math.PI; // radians

    // internals
    var scope = this;

    var gravity = 9.8
    var cameraH = camera.position.y

    var cameraP = camera.position
    var mouseSlide = false
    var moveX = 0, // 把鼠标屏幕上的横向偏移量，作为 旋转角度的基准
        moveY = 0, // 把鼠标在屏幕上的纵向偏移量 组委旋转角度的基准
        target = new THREE.Vector3(), // 相机看向的方向
        v = 0.1 // 转动速度
    var startX, startY;

    var spherical = new Spherical();

    var moveForward = false,
        moveBackward = false,
        moveLeft = false,
        moveRight = false,
        canJump = true,
        moveV = 1;

    this.startFun = undefined
    this.moveFun = undefined
    this.endFun = undefined

    function start(e) {
        scope.domElement.setAttribute('tabindex', '0')
        if (scope.startFun) scope.startFun(e)

        startX = e.pageX || e.touches[0].pageX
        startY = e.pageY || e.touches[0].pageY
    }

    function move(e) {
        if (scope.moveFun) scope.moveFun(e)

        if (mouseSlide || e.touches) {

            const pageX = e.pageX || e.touches[0].pageX, pageY = e.pageY || e.touches[0].pageY
            moveX = moveX + (startX - pageX) * v
            moveY = moveY + (pageY - startY) * v
            if (moveY >= THREE.MathUtils.radToDeg(scope.maxPolarAngle - Math.PI / 2) - .001) {
                moveY = THREE.MathUtils.radToDeg(scope.maxPolarAngle - Math.PI / 2) - .001
            } else if (moveY <= THREE.MathUtils.radToDeg(scope.minPolarAngle - Math.PI / 2) + .001) {
                moveY = THREE.MathUtils.radToDeg(scope.minPolarAngle - Math.PI / 2) + .001
            }

            startX = pageX
            startY = pageY
        }
    }

    function end(e) {
        if (scope.endFun) scope.endFun(e)
    }

    {
        scope.domElement.addEventListener('keydown', onKeyDown, false)
        scope.domElement.addEventListener('keyup', onKeyUp, false)

        scope.domElement.addEventListener('touchstart', start, false)
        scope.domElement.addEventListener('touchmove', move, false)
        scope.domElement.addEventListener('touchend', end, false)
        scope.domElement.addEventListener('mousedown', function (ev) {
            start(ev)
            mouseSlide = true
            scope.domElement.addEventListener('mousemove', move, false)
        }, false)
        scope.domElement.addEventListener('mouseup', function (e) {
            mouseSlide = false
            end(e)
        }, false)
        scope.domElement.addEventListener('mouseout', function () {
            mouseSlide = false
        }, false)
    }

    this.dispose = function () {
        scope.domElement.removeEventListener('keydown', onKeyDown)
        scope.domElement.removeEventListener('keyup', onKeyUp)

        scope.domElement.removeEventListener('touchstart', start);
        scope.domElement.removeEventListener('touchmove', move);
        scope.domElement.removeEventListener('touchend', end)

        scope.domElement.removeEventListener('mousedown', start);
        scope.domElement.removeEventListener('mousemove', move);
        scope.domElement.removeEventListener('mouseup', end)
    };

    this.getObject = function () { // retaining this method for backward compatibility 保留此方法以实现向后兼容
        return camera;
    };

    this.getDirection = function () {
        spherical.phi = THREE.MathUtils.degToRad(moveY)
        spherical.theta = THREE.MathUtils.degToRad(moveX)
        return spherical
    };

    function jumpCamera(camera, cameraH, gravity, time, tween) {
        const jumpH = 9.8 * time - gravity * time * time / 2
        if (jumpH <= 0) {
            camera.position.setY(cameraH)
        } else {
            camera.position.setY(cameraH + jumpH)
        }
    }

    function onKeyDown(event) {
        switch (event.code) {
            case 'ArrowUp':
            case 'KeyW':
                moveForward = true;
                break;

            case 'ArrowLeft':
            case 'KeyA':
                moveLeft = true;
                break;

            case 'ArrowDown':
            case 'KeyS':
                moveBackward = true;
                break;

            case 'ArrowRight':
            case 'KeyD':
                moveRight = true;
                break;

            case 'Space':
                if (canJump === true) {
                    new TWEEN.Tween({time: 0})
                        .to({time: 1000})
                        .onUpdate(val => {
                            jumpCamera(camera, cameraH, gravity, val.time / 500)
                        }).onComplete(val => {
                        canJump = true
                    }).easing(TWEEN.Easing.Linear.None).start();
                }
                canJump = false;
                break;

            case 'ShiftLeft':
            case 'ShiftRight':
                moveV = 3;
                break;
        }
    };

    function onKeyUp(event) {

        switch (event.code) {

            case 'ArrowUp':
            case 'KeyW':
                moveForward = false;
                break;

            case 'ArrowLeft':
            case 'KeyA':
                moveLeft = false;
                break;

            case 'ArrowDown':
            case 'KeyS':
                moveBackward = false;
                break;

            case 'ArrowRight':
            case 'KeyD':
                moveRight = false;
                break;

            case 'ShiftLeft':
            case 'ShiftRight':
                moveV = 1;
                break;
        }

    };

    this.moveForward = function (distance) {
        camera.position.z -= distance * Math.cos(THREE.MathUtils.degToRad(moveX)) * moveV
        camera.position.x += distance * Math.sin(THREE.MathUtils.degToRad(moveX)) * moveV
    };

    this.moveRight = function (distance) {
        camera.position.x += distance * Math.cos(THREE.MathUtils.degToRad(moveX)) * moveV
        camera.position.z += distance * Math.sin(THREE.MathUtils.degToRad(moveX)) * moveV
    };

    this.getPosition = function () {
        return camera.position
    }

    this.fromMoveTo = function (from, to, time) {
        new TWEEN.Tween(from).to(to, time || 1000).easing(TWEEN.Easing.Sinusoidal.InOut).start();
    }

    this.update = function () {

        target.setX(cameraP.x + Math.cos(THREE.MathUtils.degToRad(moveX - 90)) * Math.cos(THREE.MathUtils.degToRad(moveY)))
        target.setY(cameraP.y + Math.sin(THREE.MathUtils.degToRad(moveY)))
        target.setZ(cameraP.z + Math.sin(THREE.MathUtils.degToRad(moveX - 90)) * Math.cos(THREE.MathUtils.degToRad(moveY)))
        camera.lookAt(target)

        const s = .25
        const forward = moveForward ? s : moveBackward ? -s : 0;
        const left = moveLeft ? -s : moveRight ? s : 0;
        if (forward) scope.moveForward(forward);
        if (left) scope.moveRight(left);

        TWEEN.update()
    }
    setInterval(ev => {
        // console.log(moveX)
    }, 1000)
};

MoveLookAtControls.prototype = Object.create(EventDispatcher.prototype);
MoveLookAtControls.prototype.constructor = MoveLookAtControls;

export {MoveLookAtControls};
